using Sensor.Common.Buffer;
using Sensor.SerialPorts.Enums;
using System;
using System.IO;
using System.IO.Ports;
using System.Threading;
using System.Threading.Tasks;

namespace Sensor.SerialPorts;

/// <summary>
/// 串口管理器，提供串口通信的完整功能，包括异步发送、接收和数据处理
/// </summary>
/// <remarks>
/// 此类使用循环缓冲区管理数据，支持高吞吐量的串口通信。
/// 所有I/O操作都是异步的，不会阻塞调用线程。
/// </remarks>
public class SerialPortManager : IDisposable
{
    #region 常量定义
    /// <summary>
    /// 默认缓冲区大小倍数，用于根据波特率计算缓冲区大小
    /// </summary>
    private const int DefaultBufferMultiplier = 3;
    
    /// <summary>
    /// 串口读写缓冲区大小的倍数，用于设置内部缓冲区大小
    /// </summary>
    private const int InternalBufferMultiplier = 2;
    
    /// <summary>
    /// 默认读写超时时间（毫秒）
    /// </summary>
    private const int DefaultTimeoutMs = 500;
    
    /// <summary>
    /// 工作线程空闲时的延迟时间（毫秒）
    /// </summary>
    private const int WorkerThreadDelayMs = 10;
    
    /// <summary>
    /// 错误处理延迟时间（毫秒）
    /// </summary>
    private const int ErrorDelayMs = 100;
    
    /// <summary>
    /// 临时缓冲区大小
    /// </summary>
    private const int TempBufferSize = 1024;
    
    /// <summary>
    /// 发送缓冲区警告阈值（百分比）
    /// </summary>
    private const double SendBufferWarningThreshold = 0.9;
    
    /// <summary>
    /// 等待任务完成的超时时间（毫秒）
    /// </summary>
    private const int TaskWaitTimeoutMs = 1000;
    #endregion

    #region 私有字段
    /// <summary>
    /// 串口对象
    /// </summary>
    private readonly SerialPort _serialPort = new();
    
    /// <summary>
    /// 标识是否已释放资源
    /// </summary>
    private bool _disposed = false;

    /// <summary>
    /// 接收数据缓冲区
    /// </summary>
    private ConcurrentCircularBuffer<byte>? _receiveBuffer;
    
    /// <summary>
    /// 发送数据缓冲区
    /// </summary>
    private ConcurrentCircularBuffer<byte>? _sendBuffer;
    
    /// <summary>
    /// 用于取消异步操作的令牌源
    /// </summary>
    private CancellationTokenSource? _cts;
    
    /// <summary>
    /// 接收数据任务
    /// </summary>
    private Task? _receiveTask;
    
    /// <summary>
    /// 发送数据任务
    /// </summary>
    private Task? _sendTask;
    
    /// <summary>
    /// 处理数据任务
    /// </summary>
    private Task? _processTask;
    
    /// <summary>
    /// 用于同步访问的锁对象
    /// </summary>
    private readonly object _lockObject = new();
    
    /// <summary>
    /// 指示是否需要在UI线程上触发事件
    /// </summary>
    private readonly bool _raiseEventsOnUIThread;
    
    /// <summary>
    /// 用于在UI线程上触发事件的同步上下文
    /// </summary>
    private readonly SynchronizationContext? _synchronizationContext;
    #endregion

    #region 公共属性
    /// <summary>
    /// 获取串口是否已打开
    /// </summary>
    public bool IsOpen => _serialPort.IsOpen;
    
    /// <summary>
    /// 获取当前接收缓冲区中的数据量
    /// </summary>
    public int ReceiveBufferCount => _receiveBuffer?.Count ?? 0;
    
    /// <summary>
    /// 获取当前发送缓冲区中的数据量
    /// </summary>
    public int SendBufferCount => _sendBuffer?.Count ?? 0;
    
    #endregion

    #region 事件
    /// <summary>
    /// 接收到原始字节数据帧事件（二进制帧）
    /// </summary>
    public event Action<byte[]>? BinaryFrameReceived;

    /// <summary>
    /// 通信错误事件
    /// </summary>
    public event Action<string>? ErrorOccurred;
    #endregion

    #region 构造函数
    /// <summary>
    /// 初始化 SerialPortManager 类的新实例
    /// </summary>
    /// <param name="raiseEventsOnUIThread">是否需要在UI线程上触发事件，默认为false</param>
    public SerialPortManager(bool raiseEventsOnUIThread = false)
    {
        _raiseEventsOnUIThread = raiseEventsOnUIThread;
        _synchronizationContext = raiseEventsOnUIThread ? SynchronizationContext.Current : null;
    }
    #endregion

    #region 公共方法
    /// <summary>
    /// 异步打开串口连接
    /// </summary>
    /// <param name="port">串口名称</param>
    /// <param name="baudRate">波特率</param>
    /// <param name="parityBit">校验位</param>
    /// <param name="stopBit">停止位</param>
    /// <param name="dataBit">数据位</param>
    /// <param name="cancellationToken">用于取消操作的取消令牌</param>
    /// <returns>表示异步操作的任务</returns>
    /// <exception cref="ArgumentException">当参数无效时抛出</exception>
    /// <exception cref="InvalidOperationException">当串口操作失败时抛出</exception>
    /// <exception cref="UnauthorizedAccessException">当访问串口被拒绝时抛出</exception>
    /// <exception cref="IOException">当串口IO错误时抛出</exception>
    /// <exception cref="OperationCanceledException">当操作被取消时抛出</exception>
    public Task OpenAsync(string port, int baudRate, ParityBit parityBit, StopBit stopBit, int dataBit, CancellationToken cancellationToken = default)
    {
        // 检查取消请求
        cancellationToken.ThrowIfCancellationRequested();
        
        // 参数验证
        ArgumentException.ThrowIfNullOrEmpty(port);
        ArgumentOutOfRangeException.ThrowIfNegativeOrZero(baudRate);
        ArgumentOutOfRangeException.ThrowIfNegativeOrZero(dataBit);
        
        if (dataBit < 5 || dataBit > 9)
        {
            throw new ArgumentOutOfRangeException(nameof(dataBit), "数据位必须在5-9之间");
        }

        try
        {
            lock (_lockObject)
            {
                // 如果串口已打开，先关闭
                if (IsOpen) 
                {
                    Close();
                }

                // 根据波特率计算缓冲区大小
                var bufferSize = baudRate * DefaultBufferMultiplier;
                
                // 初始化缓冲区和分包器
                _receiveBuffer = new ConcurrentCircularBuffer<byte>(bufferSize);
                _sendBuffer = new ConcurrentCircularBuffer<byte>(bufferSize);

                // 初始化取消令牌
                _cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);

                // 配置串口参数
                _serialPort.PortName = port;
                _serialPort.BaudRate = baudRate;
                _serialPort.Parity = SerialPortHelp.GetParity(parityBit.ToString());
                _serialPort.StopBits = SerialPortHelp.GetStopBits(stopBit.ToString());
                _serialPort.DataBits = dataBit;

                // 设置串口缓冲区大小
                _serialPort.ReadBufferSize = bufferSize * InternalBufferMultiplier;  
                _serialPort.WriteBufferSize = bufferSize * InternalBufferMultiplier; 

                // 优化设置以处理高速数据传输
                _serialPort.ReadTimeout = DefaultTimeoutMs;
                _serialPort.WriteTimeout = DefaultTimeoutMs;
                
                
                // 打开串口
                _serialPort.Open();

                // 启动工作线程
                StartWorkerTasks();
            }
        }
        catch (UnauthorizedAccessException ex)
        {
            throw new InvalidOperationException($"访问串口 {port} 被拒绝，可能已被其他程序占用", ex);
        }
        catch (ArgumentOutOfRangeException ex)
        {
            throw new InvalidOperationException($"无效的串口参数: {ex.Message}", ex);
        }
        catch (IOException ex)
        {
            throw new InvalidOperationException($"串口 {port} IO错误: {ex.Message}", ex);
        }
        catch (Exception ex)
        {
            RaiseError($"打开串口失败: {ex.Message}");
            throw new InvalidOperationException($"打开串口失败: {ex.Message}", ex);
        }

        return Task.CompletedTask;
    }

    /// <summary>
    /// 打开串口连接
    /// </summary>
    /// <param name="port">串口名称</param>
    /// <param name="baudRate">波特率</param>
    /// <param name="parityBit">校验位</param>
    /// <param name="stopBit">停止位</param>
    /// <param name="dataBit">数据位</param>
    /// <exception cref="ArgumentException">当参数无效时抛出</exception>
    /// <exception cref="InvalidOperationException">当串口操作失败时抛出</exception>
    public void Open(string port, int baudRate, ParityBit parityBit, StopBit stopBit, int dataBit)
    {
        ObjectDisposedException.ThrowIf(_disposed, this);
        
        OpenAsync(port, baudRate, parityBit, stopBit, dataBit).GetAwaiter().GetResult();
    }

    /// <summary>
    /// 异步关闭串口连接
    /// </summary>
    /// <param name="cancellationToken">用于取消操作的取消令牌</param>
    /// <returns>表示异步操作的任务</returns>
    /// <exception cref="OperationCanceledException">当操作被取消时抛出</exception>
    public async Task CloseAsync(CancellationToken cancellationToken = default)
    {
        // 检查取消请求
        cancellationToken.ThrowIfCancellationRequested();
        
        if (!IsOpen || _serialPort == null) return;

        try
        {
            lock (_lockObject)
            {
                // 取消所有异步操作
                _cts?.Cancel();
            }
            
            // 等待所有任务完成
            var tasks = new[] { _receiveTask, _sendTask, _processTask };
            var nonNullTasks = tasks.Where(t => t != null).Cast<Task>().ToArray();
            if (nonNullTasks.Length > 0)
            {
                try
                {
                    await Task.WhenAll(nonNullTasks).ConfigureAwait(false);
                }
                catch (OperationCanceledException)
                {
                    // 任务被取消是正常情况
                }
            }
            
            // 关闭串口
            if (_serialPort.IsOpen)
            {
                _serialPort.Close();
            }
        }
        catch (AggregateException ex)
        {
            // 处理任务等待中的异常
            var flattened = ex.Flatten();
            foreach (var innerEx in flattened.InnerExceptions)
            {
                RaiseError($"关闭串口时任务异常: {innerEx.Message}");
            }
        }
        catch (Exception ex)
        {
            RaiseError($"关闭串口失败: {ex.Message}");
        }
    }

    /// <summary>
    /// 关闭串口连接
    /// </summary>
    public void Close()
    {
        CloseAsync().GetAwaiter().GetResult();
    }

    /// <summary>
    /// 异步发送数据到串口
    /// </summary>
    /// <param name="data">要发送的字节数组</param>
    /// <param name="cancellationToken">用于取消操作的取消令牌</param>
    /// <returns>发送是否成功</returns>
    /// <exception cref="ArgumentNullException">当data为null时抛出</exception>
    /// <exception cref="ArgumentException">当data为空数组时抛出</exception>
    /// <exception cref="InvalidOperationException">当串口未打开时抛出</exception>
    /// <exception cref="ObjectDisposedException">当对象已释放时抛出</exception>
    /// <exception cref="OperationCanceledException">当操作被取消时抛出</exception>
    public Task<bool> SendAsync(byte[] data, CancellationToken cancellationToken = default)
    {
        // 检查取消请求
        cancellationToken.ThrowIfCancellationRequested();
        
        // 参数验证
        ArgumentNullException.ThrowIfNull(data);
        if (data.Length == 0)
        {
            throw new ArgumentException("数据不能为空", nameof(data));
        }

        // 检查对象状态
        ObjectDisposedException.ThrowIf(_serialPort == null, nameof(SerialPortManager));
        
        if (!IsOpen || _sendBuffer == null)
        {
            throw new InvalidOperationException("串口未打开");
        }

        try
        {
            lock (_lockObject)
            {
                // 检查发送缓冲区是否接近满载
                if (_sendBuffer.Count + data.Length > _sendBuffer.Capacity * SendBufferWarningThreshold)
                {
                    RaiseError("发送缓冲区即将满，可能导致发送延迟");
                }
                
                // 将数据写入发送缓冲区
                int written = _sendBuffer.EnqueueRange(data, 0, data.Length);
                return Task.FromResult(written == data.Length);
            }
        }
        catch (Exception ex)
        {
            RaiseError($"发送数据失败: {ex.Message}");
            throw;
        }
    }

    /// <summary>
    /// 发送数据到串口
    /// </summary>
    /// <param name="data">要发送的字节数组</param>
    /// <returns>发送是否成功</returns>
    /// <exception cref="ArgumentNullException">当data为null时抛出</exception>
    public bool Send(byte[] data)
    {
        return SendAsync(data).GetAwaiter().GetResult();
    }
    #endregion

    #region 私有方法
    /// <summary>
    /// 启动工作线程（接收、发送、处理）
    /// </summary>
    private void StartWorkerTasks()
    {
        if (_cts == null)
        {
            return;
        }

        // 重置取消令牌
        _cts.TryReset();
        
        // 启动接收数据任务
        _receiveTask = Task.Run(async () => await ReceiveLoopAsync(_cts.Token), _cts.Token);
        
        // 启动发送数据任务
        _sendTask = Task.Run(async () => await SendLoopAsync(_cts.Token), _cts.Token);
        
        // 启动处理数据任务
        _processTask = Task.Run(async () => await ProcessLoopAsync(_cts.Token), _cts.Token);
    }

    /// <summary>
    /// 接收循环（从串口读取原始字节到缓冲区）
    /// </summary>
    /// <param name="cancellationToken">用于取消操作的取消令牌</param>
    /// <remarks>
    /// 此方法在循环中只尝试读取一次数据，有数据时立即读取，无数据时短暂延迟后继续下一次循环
    /// 这样可以避免竞态条件，提高数据读取的可靠性
    /// </remarks>
    private async Task ReceiveLoopAsync(CancellationToken cancellationToken = default)
    {
        if (_cts == null || _serialPort == null || _receiveBuffer == null)
        {
            return;
        }
        
        var buffer = new byte[TempBufferSize];
        
        while (!cancellationToken.IsCancellationRequested && IsOpen)
        {
            try
            {
                // 直接尝试读取数据，不预先检查 BytesToRead
                // 在循环中只尝试读取一次数据，有数据时立即读取，无数据时短暂延迟后继续下一次循环
                int bytesRead = 0;

                if (_serialPort.BytesToRead > 0)
                {
                    // 只在有数据可读时才尝试读取
                    bytesRead = _serialPort.Read(buffer, 0, buffer.Length);
                }

                if (bytesRead > 0)
                {
                    // 将读取的数据写入接收缓冲区
                    int written = _receiveBuffer.EnqueueRange(buffer, 0, bytesRead);
                    if (written < bytesRead)
                    {
                        RaiseError($"接收缓冲区溢出，丢失 {bytesRead - written} 字节数据");
                    }
                }
                else
                {
                    // 没有数据可读，短暂延迟后继续
                    await Task.Delay(WorkerThreadDelayMs, cancellationToken).ConfigureAwait(false);
                }
            }
            catch (OperationCanceledException)
            {
                // 任务被取消，循环条件会在下一次迭代时检查并退出循环
                // 不需要break语句，因为循环条件已经包含了取消检查
            }
            catch (Exception ex)
            {
                RaiseError($"接收数据错误: {ex.Message}");
                await Task.Delay(ErrorDelayMs, cancellationToken).ConfigureAwait(false);
            }
        }
    }

    /// <summary>
    /// 发送循环（从缓冲区读取数据并发送到串口）
    /// </summary>
    /// <param name="cancellationToken">用于取消操作的取消令牌</param>
    /// <remarks>
    /// 此方法从发送缓冲区读取数据并异步写入串口
    /// 使用 FlushAsync 确保数据被实际发送
    /// </remarks>
    private async Task SendLoopAsync(CancellationToken cancellationToken = default)
    {
        if (_cts == null || _serialPort == null || _sendBuffer == null)
        {
            return;
        }

        var buffer = new byte[TempBufferSize];
        
        while (!cancellationToken.IsCancellationRequested && IsOpen)
        {
            try
            {
                // 检查发送缓冲区是否有数据
                if (_sendBuffer.IsEmpty)
                {
                    await Task.Delay(WorkerThreadDelayMs, cancellationToken).ConfigureAwait(false);
                    continue;
                }

                // 从发送缓冲区读取数据
                int bytesToSend = _sendBuffer.DequeueRange(buffer, 0, buffer.Length);
                if (bytesToSend > 0)
                {
                    // 异步写入串口
                    await _serialPort.BaseStream.WriteAsync(buffer.AsMemory(0, bytesToSend), cancellationToken).ConfigureAwait(false);

                    // 确保数据被发送
                    await _serialPort.BaseStream.FlushAsync(cancellationToken).ConfigureAwait(false);
                }
            }
            catch (OperationCanceledException)
            {
                // 任务被取消，循环条件会在下一次迭代时检查并退出循环
                // 不需要break语句，因为循环条件已经包含了取消检查
            }
            catch (TimeoutException)
            {
                // 发送超时，继续循环
            }
            catch (Exception ex)
            {
                RaiseError($"发送数据错误: {ex.Message}");
                await Task.Delay(ErrorDelayMs, cancellationToken).ConfigureAwait(false);
            }
        }
    }

    /// <summary>
    /// 处理循环（从接收缓冲区读取数据并触发事件）
    /// </summary>
    /// <param name="cancellationToken">用于取消操作的取消令牌</param>
    /// <remarks>
    /// 此方法从接收缓冲区读取数据并触发 BinaryFrameReceived 事件
    /// 使用临时缓冲区读取数据，然后创建实际大小的数据数组
    /// </remarks>
    private async Task ProcessLoopAsync(CancellationToken cancellationToken = default)
    {
        if (_cts == null || _receiveBuffer == null)
        {
            return;
        }

        // 在循环外部创建临时缓冲区，避免在每次迭代中创建新对象
        var tempBuffer = new byte[TempBufferSize];
        
        while (!cancellationToken.IsCancellationRequested && IsOpen)
        {
            try
            {
                // 检查接收缓冲区是否有数据
                if (_receiveBuffer.IsEmpty)
                {
                    await Task.Delay(WorkerThreadDelayMs, cancellationToken).ConfigureAwait(false);
                    continue;
                }

                // 从接收缓冲区读取数据
                int bytesRead = _receiveBuffer.DequeueRange(tempBuffer, 0, tempBuffer.Length);
                
                if (bytesRead > 0)
                {
                    // 创建实际大小的数据数组
                    var data = new byte[bytesRead];
                    Array.Copy(tempBuffer, data, bytesRead);

                    // 根据配置触发数据接收事件
                    if (_raiseEventsOnUIThread && _synchronizationContext != null)
                    {
                        _synchronizationContext.Post(_ => BinaryFrameReceived?.Invoke(data), null);
                    }
                    else
                    {
                        BinaryFrameReceived?.Invoke(data);
                    }
                }
            }
            catch (OperationCanceledException)
            {
                // 任务被取消，循环条件会在下一次迭代时检查并退出循环
                // 不需要break语句，因为循环条件已经包含了取消检查
            }
            catch (Exception ex)
            {
                RaiseError($"数据处理错误: {ex.Message}");
                await Task.Delay(ErrorDelayMs, cancellationToken).ConfigureAwait(false);
            }
        }
    }

    /// <summary>
    /// 触发错误事件并记录错误日志
    /// </summary>
    /// <param name="message">错误消息</param>
    /// <remarks>
    /// 此方法触发 ErrorOccurred 事件，将错误信息传递给订阅者
    /// </remarks>
    private void RaiseError(string message)
    {
        if (_raiseEventsOnUIThread && _synchronizationContext != null)
        {
            _synchronizationContext.Post(_ => ErrorOccurred?.Invoke(message), null);
        }
        else
        {
            ErrorOccurred?.Invoke(message);
        }
    }
    #endregion

    #region 资源释放
    /// <summary>
    /// 释放资源
    /// </summary>
    /// <param name="disposing">是否正在释放托管资源</param>
    /// <remarks>
    /// 如果 disposing 为 true，则释放所有托管和非托管资源
    /// 如果 disposing 为 false，则仅释放非托管资源
    /// </remarks>
    protected virtual void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            if (disposing)
            {
                // 释放托管资源
                try
                {
                    // 关闭串口连接
                    Close();
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"关闭串口时发生错误: {ex.Message}");
                }
                
                // 释放取消令牌
                if (_cts is IDisposable disposableToken)
                {
                    disposableToken.Dispose();
                    _cts = null;
                }
                
                // 释放串口资源
                if (_serialPort is IDisposable disposablePort)
                {
                    disposablePort.Dispose();
                }
            }

            // 释放未托管的资源（如果有的话）
            // 将大型字段设置为 null
            _receiveBuffer = null;
            _sendBuffer = null;
            _receiveTask = null;
            _sendTask = null;
            _processTask = null;
            
            _disposed = true;
        }
    }

    /// <summary>
    /// 析构函数，释放未托管资源
    /// </summary>
    /// <remarks>
    /// 仅在未调用 Dispose 方法时由垃圾回收器调用
    /// </remarks>
    ~SerialPortManager()
    {
        // 不要更改此代码。请将清理代码放入"Dispose(bool disposing)"方法中
        Dispose(disposing: false);
    }

    /// <summary>
    /// 释放所有资源
    /// </summary>
    /// <remarks>
    /// 调用此方法后，对象将不再可用
    /// </remarks>
    public void Dispose()
    {
        // 不要更改此代码。请将清理代码放入"Dispose(bool disposing)"方法中
        Dispose(disposing: true);
        
        // 防止析构函数被调用
        GC.SuppressFinalize(this);
    }
    #endregion
}
